feat(critic): manifesto compliance check gates auto-merge (#133)#141
Merged
Merged
Conversation
The critic could rubber-stamp PRs that compiled and passed tests but
visibly violated project quality rules (e.g. `except Exception: pass`,
weak assertions). With nothing teaching the critic about the
manifestos, auto-merge shipped drift.
This wires the critic into the manifestos:
- `docs/manifestos/` is now the canonical home for project rules. Two
seed manifestos (error-handling, testing) ship with the repo so the
feature is operative on day one.
- `_critic_sdk.load_manifestos_text` reads every `*.md`/`*.txt` file
under the canonical dir (overridable via `LOOP_MANIFESTOS_DIR`) and
renders one block per file. `critic.review_pr` interpolates that
block into `briefs/critic.md.tmpl` via a new `{manifestos}` slot.
- `CriticReport` gains a `manifesto_violations: list[ManifestoViolation]`
field. `ManifestoViolation` is a typed dataclass with rule_id,
manifesto, quote, suggested_fix, severity. Back-compat: JSON without
the field still parses (defaults to `[]`).
- `_coerce_report` drops malformed entries (missing rule_id/manifesto),
treats unknown severities as sev3 (defensive default), and forces
`overall = "request_changes"` whenever any sev1 violation appears —
even if the model emitted `approve`.
- `critic_actions.plan_actions` blocks auto-merge on any sev1 manifesto
violation, labels the PR `critic:blocking` + `critic:manifesto-violation`,
and posts a manifesto-violation summary comment via the gh client.
sev2/sev3 violations are reported but not blocking.
- Prompt template now teaches the model to scan for manifesto rule
violations and emit the new JSON shape.
Tests (`tests/test_critic_manifesto_check.py`):
- happy-path: manifesto text rendered into prompt; violations field
parses; back-compat with absent field; sev1 flips overall;
sev2 leaves it alone.
- adversarial: malformed entry skipped; unknown severity → sev3;
wrong-type field ignored.
- gate: sev1 violation blocks; sev2-only does not.
Existing `tests/test_briefs.py::test_render_critic_brief_*` adjusted to
pass the new required `manifestos` kwarg.
Owner
Author
|
Source issue #133 was closed mid-flight (state: closed). Loop refusing auto-merge. Reopen the issue OR merge manually. |
hadamrd
added a commit
that referenced
this pull request
May 28, 2026
…loop) (#149) Closes the feedback loop the CTO described: every bug we fix becomes a permanent gate. Today's PR #147 (critic SDK event-capture mismatch) exposed a 4-PR train of bugs with the same shape — #97, #120, #128, #147 — all driven by string-literal discriminators that didn't match across module boundaries. The critic (PR #141) reads the quality manifesto + flags sev1 violations. This rule + the critic infrastructure together mean the next worker that writes ``event["type"] == "result"`` (or similar cross-module string-comparison) gets the PR auto-blocked with the manifesto rationale.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #133.
Summary
_critic_sdk.load_manifestos_textreads every file underdocs/manifestos/(overridable viaLOOP_MANIFESTOS_DIR) and renders them into the critic prompt via a new{manifestos}placeholder inbriefs/critic.md.tmpl.CriticReportgainsmanifesto_violations: list[ManifestoViolation]— typed dataclass withrule_id,manifesto,quote,suggested_fix,severity._coerce_reportparses the new field with full back-compat (missing →[]), drops malformed entries, defensively defaults unknown severities to sev3, and forcesoverall = "request_changes"whenever any sev1 manifesto violation is present — even if the model emittedapprove.critic_actions.plan_actionsblocks auto-merge on any sev1 manifesto violation and labels the PRcritic:manifesto-violationin addition tocritic:blocking.apply_critic_reportposts a manifesto-violation summary comment.docs/manifestos/error-handling.md,testing.md) so the feature is operative immediately.Acceptance criteria mapping
_critic_sdk.pyloads every manifesto + renders into prompt viacritic.md.tmpl—load_manifestos_text+ new{manifestos}placeholder.CriticReport.manifesto_violations: list[ManifestoViolation]with full field set.[]for back-compat.request_changesand blocks auto-merge.Test plan
tests/test_critic_manifesto_check.py— happy + adversarial + gate (12 cases).tests/test_critic.py— existing critic suite still green.tests/test_briefs.py— updated for new requiredmanifestoskwarg.🤖 Generated with Claude Code